package com.ll19.dao.jpa;

import java.util.List;

import javax.persistence.Query;
import javax.persistence.Table;
import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ll19.dao.UserDao;
import com.ll19.model.User;

/**
 * This class interacts with Spring's HibernateTemplate to save/delete and
 * retrieve User objects.
 * 
 * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a> Modified by
 *         <a href="mailto:dan@getrolling.com">Dan Kibler</a> Extended to
 *         implement Acegi UserDetailsService interface by David Carter
 *         david@carter.net Modified by <a href="mailto:bwnoll@gmail.com">Bryan
 *         Noll</a> to work with the new BaseDaoHibernate implementation that
 *         uses generics.
 */
// spring组件扫描context:component-scan
// @Repository、@Service和
// @Controller是@Component的细化，用来表示更具体的用例(分别对应了持久化层、服务层和表现层)。
@Repository("userDao")
public class UserDaoJpa extends GenericDaoJpa<User, Long> implements UserDao,
		UserDetailsService {

	// spring注释注入
	// @Autowired(默认按类型查找，若没有查到 不会再去用名称查找)
	// @Autowired 是按 byType 自动注入,如果想 byName ,可以使用 @Qualifier
	// @Autowired
	// @Qualifier("beanName")
	// @Resource 默认按 byName 自动注入,只是它是由 JSR-250 规范定义的注释
	// 使用注入后,就不用再 setter/getter 了
	@Autowired
	private DataSource dataSource;

	/**
	 * Constructor that sets the entity to User.class.
	 */
	public UserDaoJpa() {
		super(User.class);
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	public List<User> getUsers() {
		Query q = getEntityManager().createQuery(
				"select u from User u order by upper(u.username)");
		return q.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("unchecked")
	@Transactional
	public UserDetails loadUserByUsername(String username)
			throws UsernameNotFoundException {
		Query q = getEntityManager().createQuery(
				"select u from User u where username=?");
		q.setParameter(1, username);
		List<User> users = q.getResultList();
		if (users == null || users.isEmpty()) {
			throw new UsernameNotFoundException("user '" + username
					+ "' not found...");
		} else {
			return users.get(0);
		}
	}

	/**
	 * Save user and flush entityManager
	 * 
	 * @param user
	 *            the user to save
	 * @return the updated user
	 */
	public User saveUser(User user) {
		User u = super.save(user);
		// 将持久化态的实体数据同步到数据库中
		// 为了只在当事务提交时才将改变更新到数据库中，容器将所有数据库操作集中到一个批处理中，这样就减少了代价昂贵的与数据库的交互。
		// 当你调用persist( ), merge( )或 remove(
		// )这些方法时，更新并不会立刻同步到数据库中，直到容器决定刷新到数据库中时才会执行。
		// 如果你需要在事务提交之前将更新刷新到数据库中，你可以直接地调用EntityManager.flush()方法。
		// 于是事务管理器将不会在方法的结尾(也就是事务的结尾)处刷新更新到数据库中。这种情况下，你可以手工地来刷新数据库以获得对数据库操作的最大控制。

		// 由于hibernate是批处理机制
		// 在你执行多步数据库操作的时候 比如 插入user表数据 之后再查询 则需要flush() 否则无法查询出当前这些数据
		// flush只是同步数据库数据 不会提交事务 不会关闭entityma
		// flush在commit之前默认都会执行他。也可以手动执行它，他主要做了两件事：
		// 1） 清理缓存。
		// 2） 执行SQL。

		/**
		 * @TODO 根据你其它业务需求 可能需要及时执行 flush
		 */

		getEntityManager().flush();
		return u;
	}

	/**
	 * {@inheritDoc}
	 */
	public String getUserPassword(String username) {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		// SimpleJdbcTemplate jdbcTemplate = new SimpleJdbcTemplate(dataSource);
		Table table = AnnotationUtils.findAnnotation(User.class, Table.class);
		return jdbcTemplate.queryForObject(
				"select password from " + table.name() + " where username=?",
				String.class, username);
	}
}
